package me.leoray.leilog;

/**
 * 会显示一些额外的信息:包括方法书,线程信息,调用栈等
 * Created by leoraylei on 16/8/28.
 */
public class DetailLogDecoration implements LogDecoration {

    private static final int METHOD_OFFSET_BASIC = 8; //基本的方法偏移,日志框架中的方法调用信息没有啥用

    //显示线程信息
    protected boolean showThreadInfo = true;
    //显示方法信息
    protected int methodCount = 2;    //显示的方法数,,如果为0则表示不显示方法链信息
    protected int methodOffset = 0;   //方法偏移，向上偏移的层次。如果要基于这个类创建新的日志类，可以将方法再向上偏移，否则就设置为0

    public DetailLogDecoration showThreadInfo() {
        showThreadInfo = true;
        return this;
    }

    public DetailLogDecoration hideThreadInfo() {
        showThreadInfo = false;
        return this;
    }

    public DetailLogDecoration methodOffset(int offset) {
        this.methodOffset = offset;
        return this;
    }

    public DetailLogDecoration method(int methodCount) {
        this.methodCount = methodCount;
        return this;
    }

    @Override
    public String[] process(int level, String tag, String msg) {
        StringBuilder builder = new StringBuilder();
        if (showThreadInfo) {
            builder.append("Thread: [")
                    .append(getThreadName())
                    .append("]");
        }
        if (methodCount > 0) {
            if (showThreadInfo) {
                builder.append(',');
            }
            builder.append("Method Stack:\n")
                    .append(getMethodStack());
        }
        builder.append("\nLog Msg: ")
                .append(msg.length() > 300 ? "\n" : "")
                .append(msg);
        return new String[]{builder.toString()};
    }

    protected String getThreadName() {
        return Thread.currentThread().getName();
    }

    protected String getMethodStack() {
        StackTraceElement[] trace = Thread.currentThread().getStackTrace();
        int realStackOffset = getStackOffset(trace) + methodOffset;
        int realMethodCount = this.methodCount;
        if (this.methodCount + realStackOffset > trace.length) {
            realMethodCount = trace.length - realStackOffset - 1;
        }
        StringBuilder builder = new StringBuilder();
        for (int i = 0; i < realMethodCount; i++) {
            int stackIndex = i + realStackOffset;
            StackTraceElement element = trace[stackIndex];
            String eleInfo = getTrackElementInfo(element, i, true);
            builder.append(eleInfo);
            builder.append('\n');
        }
        return builder.toString().trim();
    }

    /**
     * 方法栈信息
     *
     * @param element
     * @param i
     * @return
     */
    protected String getTrackElementInfo(StackTraceElement element, int i, boolean withIncreasePrefix) {
        StringBuilder builder = new StringBuilder();
        if (withIncreasePrefix) {
            for (int j = 0; j < i; j++) {
                builder.append("    ");
            }
        }
        builder.append(element.getClassName().substring(element.getClassName().lastIndexOf('.') + 1)); //class name
        builder.append('.');
        builder.append(element.getMethodName()); //method name
        builder.append(' ');
        builder.append('(');
        builder.append(element.getFileName()); //跳转
        builder.append(':');
        builder.append(element.getLineNumber());
        builder.append(')');

        return builder.toString();
    }

    /**
     * 方法偏移
     *
     * @param stackTraceElements
     * @return
     */
    private static int getStackOffset(StackTraceElement[] stackTraceElements) {
        int methodOffset = METHOD_OFFSET_BASIC;
        String className = stackTraceElements[methodOffset].getClassName();
        while (className.startsWith(Logger.class.getPackage().getName())) {
            methodOffset++;
            className = stackTraceElements[methodOffset].getClassName();
        }
        return methodOffset;
    }

}
